package com.jzdjt.tool;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

public class PayCommonUtil {
	// 微信参数配置
	public static String API_KEY = "4a6764f4d796b0d854ef2d4deb4496fc";
	public static String APPID = "WX71ae045bbb69998e";
	public static String MCH_ID = "1526872541";

	// 随机字符串生成
	public static String getRandomString(int length) { // length表示生成字符串的长度
		String base = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		Random random = new Random();
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < length; i++) {
			int number = random.nextInt(base.length());
			sb.append(base.charAt(number));
		}
		return sb.toString();
	}
	
	
	/**
     *                      -
     * @param trade_no      订单号
     * @param totalAmount   支付金额
     * @param description   文字内容说明
     * @param attach        自定义参数 length=127
     * @param openid        微信公众号openId
     * @param wxnotify      回调地址
     * @param request       -
     * @return              -
     */
	private static Map<String, String> weixinPrePay(String trade_no, BigDecimal totalAmount, String description, String attach,  String wxnotify, HttpServletRequest request) {
        SortedMap<String, Object> parameterMap = new TreeMap<>();
        parameterMap.put("appid", PayCommonUtil.APPID);
        parameterMap.put("mch_id", PayCommonUtil.MCH_ID);
        parameterMap.put("nonce_str", getRandomString(32));
        parameterMap.put("body", description);
        parameterMap.put("attach", attach);
        parameterMap.put("out_trade_no", trade_no);
        parameterMap.put("fee_type", "CNY");
        BigDecimal total = totalAmount.multiply(new BigDecimal(100));
        java.text.DecimalFormat df = new java.text.DecimalFormat("0");
        parameterMap.put("total_fee", df.format(total));
        parameterMap.put("spbill_create_ip", request.getRemoteAddr());
        parameterMap.put("notify_url", wxnotify);
        parameterMap.put("trade_type", "APP"); 
        String sign = PayCommonUtil.createSign(parameterMap);
        parameterMap.put("sign", sign);
        String requestXML = PayCommonUtil.getRequestXml(parameterMap);
        String result = PayCommonUtil.httpsRequest("https://api.mch.weixin.qq.com/pay/unifiedorder", "POST", requestXML);
        System.out.println(result);
        Map<String, String> map = null;
        try {
            map = PayCommonUtil.doXMLParse(result);
        } catch (JDOMException | IOException e) {
            e.printStackTrace();
        }
        return map;
    }

	// 请求xml组装
	public static String getRequestXml(SortedMap<String, Object> parameters) {
		StringBuffer sb = new StringBuffer();
		sb.append("<xml>");
		Set es = parameters.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String key = (String) entry.getKey();
			String value = (String) entry.getValue();
			if ("attach".equalsIgnoreCase(key) || "body".equalsIgnoreCase(key) || "sign".equalsIgnoreCase(key)) {
				sb.append("<" + key + ">" + "<![CDATA[" + value + "]]></" + key + ">");
			} else {
				sb.append("<" + key + ">" + value + "</" + key + ">");
			}
		}
		sb.append("</xml>");
		return sb.toString();
	}

	/**
	 * 所有参数除sign外，按照ASCII码排序，
	 */
	public static String buildASCiiParams(Map<String, Object> params) {
		StringBuffer stringBuffer = new StringBuffer();// 存放数据
		// 按照ASCII码进行排序
		Map<String, Object> sortMap = new TreeMap<String, Object>(params);
		// 便利字典，拼接"key=value&key=value"
		for (Map.Entry<String, Object> m : sortMap.entrySet()) {
			if (stringBuffer.length() > 0) {
				stringBuffer.append("&");
			}
			stringBuffer.append(m.getKey()).append("=").append(m.getValue());
		}
		return stringBuffer.toString();
	}

	// 生成签名
	public static String createSign(String characterEncoding, SortedMap<String, Object> parameters) {
		StringBuffer sb = new StringBuffer();
		Set es = parameters.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			Object v = entry.getValue();
			if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + API_KEY);
		String sign = MD5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
		return sign;
	}

     
    

    //生成签名
    private static String createSign(SortedMap<String, Object> parameters){
        StringBuilder sb = new StringBuilder();
        Set es = parameters.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
        sb.append("key=").append(API_KEY);
        System.out.println(sb.toString());
        return MD5Utils.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
    }

    /**
     * 验证回调签名
     * @param map
     * @return
     */
    public static boolean isTenpaySign(Map<String, String> map) {
        String charset = "utf-8";
        String signFromAPIResponse = map.get("sign");
        if (signFromAPIResponse == null || signFromAPIResponse.equals("")) {
            System.out.println("API返回的数据签名数据不存在，有可能被第三方篡改!!!");
            return false;
        }
        System.out.println("服务器回包里面的签名是:" + signFromAPIResponse);
        //过滤空 设置 TreeMap
        SortedMap<String,String> packageParams = new TreeMap<>();
        for (String parameter : map.keySet()) {
            String parameterValue = map.get(parameter);
            String v = "";
            if (null != parameterValue) {
                v = parameterValue.trim();
            }
            packageParams.put(parameter, v);
        }
        StringBuilder sb = new StringBuilder();
        Set es = packageParams.entrySet();
        for (Object e : es) {
            Map.Entry entry = (Map.Entry) e;
            String k = (String) entry.getKey();
            String v = (String) entry.getValue();
            if (!"sign".equals(k) && null != v && !"".equals(v)) {
                sb.append(k).append("=").append(v).append("&");
            }
        }
        sb.append("key=").append(API_KEY);
        //将API返回的数据根据用签名算法进行计算新的签名，用来跟API返回的签名进行比较
        //算出签名
        String tobesign = sb.toString();
        String resultSign = MD5Utils.MD5Encode(tobesign, "utf-8").toUpperCase();
        String tenpaySign = packageParams.get("sign").toUpperCase();
        return tenpaySign.equals(resultSign);
    }
	// 请求方法
	public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
		try {

			URL url = new URL(requestUrl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();

			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setUseCaches(false);
			// 设置请求方式（GET/POST）
			conn.setRequestMethod(requestMethod);
			conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
			// 当outputStr不为null时向输出流写数据
			if (null != outputStr) {
				OutputStream outputStream = (OutputStream) conn.getOutputStream();
				// 注意编码格式
				outputStream.write(outputStr.getBytes("UTF-8"));
				outputStream.close();
			}
			// 从输入流读取返回内容
			InputStream inputStream = (InputStream) conn.getInputStream();
			InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
			BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
			String str = null;
			StringBuffer buffer = new StringBuffer();
			while ((str = bufferedReader.readLine()) != null) {
				buffer.append(str);
			}
			// 释放资源
			bufferedReader.close();
			inputStreamReader.close();
			inputStream.close();
			inputStream = null;
			conn.disconnect();
			return buffer.toString();
		} catch (ConnectException ce) {
			System.out.println("连接超时：{}" + ce);
		} catch (Exception e) {
			System.out.println("https请求异常：{}" + e);
		}
		return null;
	}

	// //退款的请求方法
	// public static String httpsRequest2(String requestUrl, String requestMethod,
	// String outputStr) throws Exception {
	// KeyStore keyStore = KeyStore.getInstance("PKCS12");
	// StringBuilder res = new StringBuilder("");
	// FileInputStream instream = new FileInputStream(new
	// File("/home/apiclient_cert.p12"));
	// try {
	// keyStore.load(instream, "".toCharArray());
	// } finally {
	// instream.close();
	// }
	//
	// // Trust own CA and all self-signed certs
	// SSLContext sslcontext = SSLContexts.custom()
	// .loadKeyMaterial(keyStore, "1313329201".toCharArray())
	// .build();
	// // Allow TLSv1 protocol only
	// SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
	// sslcontext,
	// new String[] { "TLSv1" },
	// null,
	// SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
	// CloseableHttpClient httpclient = HttpClients.custom()
	// .setSSLSocketFactory(sslsf)
	// .build();
	// try {
	//
	// HttpPost httpost = new
	// HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
	// httpost.addHeader("Connection", "keep-alive");
	// httpost.addHeader("Accept", "*/*");
	// httpost.addHeader("Content-Type", "application/x-www-form-urlencoded;
	// charset=UTF-8");
	// httpost.addHeader("Host", "api.mch.weixin.qq.com");
	// httpost.addHeader("X-Requested-With", "XMLHttpRequest");
	// httpost.addHeader("Cache-Control", "max-age=0");
	// httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows
	// NT 6.0) ");
	// StringEntity entity2 = new StringEntity(outputStr ,Consts.UTF_8);
	// httpost.setEntity(entity2);
	// System.out.println("executing request" + httpost.getRequestLine());
	//
	// CloseableHttpResponse response = httpclient.execute(httpost);
	//
	// try {
	// HttpEntity entity = response.getEntity();
	//
	// System.out.println("----------------------------------------");
	// System.out.println(response.getStatusLine());
	// if (entity != null) {
	// System.out.println("Response content length: " + entity.getContentLength());
	// BufferedReader bufferedReader = new BufferedReader(new
	// InputStreamReader(entity.getContent()));
	// String text;res.append(text);
	// while ((text = bufferedReader.readLine()) != null) {
	// res.append(text);
	// System.out.println(text);
	// }
	//
	// }
	// EntityUtils.consume(entity);
	// } finally {
	// response.close();
	// }
	// } finally {
	// httpclient.close();
	// }
	// return  res.toString();
	//
	// }
	// xml解析
	public static Map doXMLParse(String strxml) throws JDOMException, IOException {
		strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

		if (null == strxml || "".equals(strxml)) {
			return null;
		}

		Map m = new HashMap();

		InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
		SAXBuilder builder = new SAXBuilder();
		Document doc = builder.build(in);
		Element root = doc.getRootElement();
		List list = root.getChildren();
		Iterator it = list.iterator();
		while (it.hasNext()) {
			Element e = (Element) it.next();
			String k = e.getName();
			String v = "";
			List children = e.getChildren();
			if (children.isEmpty()) {
				v = e.getTextNormalize();
			} else {
				v = getChildrenText(children);
			}

			m.put(k, v);
		}

		// 关闭流
		in.close();

		return m;
	}

	public static String getChildrenText(List children) {
		StringBuffer sb = new StringBuffer();
		if (!children.isEmpty()) {
			Iterator it = children.iterator();
			while (it.hasNext()) {
				Element e = (Element) it.next();
				String name = e.getName();
				String value = e.getTextNormalize();
				List list = e.getChildren();
				sb.append("<" + name + ">");
				if (!list.isEmpty()) {
					sb.append(getChildrenText(list));
				}
				sb.append(value);
				sb.append("</" + name + ">");
			}
		}

		return sb.toString();
	}

}
